--名称:WC3(3路继电器控制板)
ID = 200 --WC3默认ID
Baudrate = "BAUDRATE_115200"

--Led灯色彩配置
RGB = { 0, 0, 128 } --蓝

--OLED显示屏使能
OLED_Enable = 1 

--全局变量定义
CommLedOnTimeMs = 0 --通信成功指示灯有效时间计数
SysRunTimeMs = 0 --系统运行时间计数，单位：毫秒

CoilStartAddr = 0x0000 --线圈起始地址
CoilTotalNum = 3 --线圈总数
LoopDelayMsCnt = 0

--10毫秒级延时函数(N需是10的倍数)
function DelayMS(N)
	LoopDelayMsCnt = 0
	while LoopDelayMsCnt < N do
		GC(1)
	end
end

--Oled显示定制
function OledDisplay()
	if OLED_Enable == 1 then
		LIB_0_96_OledPuts("1","1",string.format("%09d s", math.floor(SysRunTimeMs/1000)))
		
	end
end

--modbus从机通信数据处理
function ModbusSlaveProcess()
	--查询是否收到Modbus主机发来的消息
	local flag, data = LIB_Uart1Recv()
	if flag == 1 then
		--判断消息是不是发给本机，是本机的才理会
		if data[1] == ID and #data >= 5 then --Modbus本机地址(1-254)
			local calc_data = {}
			table.move(data, 1, #data - 2, 1, calc_data)
			local crc16_mb = LIB_CrcCalculate("CRC16_MODBUS", calc_data)
			if crc16_mb ~= (data[#data - 1] + data[#data] * 256) then
				print("Modbus crc error")
				--CRC校验错误，丢弃此消息
				return
			end
			local sdata = {}
			--判断Modbus功能码
			if data[2] == 0x05 then --0x05 写单个线圈
				local coil_addr = data[3] * 256 + data[4]
				--判断线圈地址是否符合范围
				if coil_addr >= CoilStartAddr and coil_addr < CoilStartAddr + CoilTotalNum then
					if CommLedOnTimeMs <= 0 then
						--通信成功指示灯点亮
						LIB_WS2812LedsCtrl("UPDATE_ONE_CACHE", 1, RGB[1], RGB[2], RGB[3])
						LIB_WS2812LedsCtrl("FLUSH")
					end
					CommLedOnTimeMs = 1000 --设置通信成功指示灯亮1000ms
					sdata[1] = data[1] --本机地址
					sdata[2] = data[2] --功能码
					sdata[3] = data[3] --线圈地址高字节
					sdata[4] = data[4] --线圈地址低字节
					sdata[5] = data[5] --线圈值高字节
					sdata[6] = data[6] --线圈值低字节
					if data[5] == 0xff and data[6] == 0x00 then
						--线圈置位
						if coil_addr == 0 then
							LIB_GpioWrite("D0", 1) --控制继电器Y0闭合
						elseif coil_addr == 1 then
							LIB_GpioWrite("D1", 1) --控制继电器Y1闭合
						elseif coil_addr == 2 then
							LIB_GpioWrite("D2", 1) --控制继电器Y2闭合
						end
					elseif data[5] == 0x00 and data[6] == 0x00 then
						--线圈复位
						if coil_addr == 0 then
							LIB_GpioWrite("D0", 0) --控制继电器Y0断开
						elseif coil_addr == 1 then
							LIB_GpioWrite("D1", 0) --控制继电器Y1断开
						elseif coil_addr == 2 then
							LIB_GpioWrite("D2", 0) --控制继电器Y2断开
						end
					end
					local CRC = LIB_CrcCalculate("CRC16_MODBUS", sdata)
					sdata[#sdata + 1] = CRC & 0x00ff --低位在前
					sdata[#sdata + 1] = CRC >> 8 --高位在后
					LIB_Uart1BlockSend(sdata)
				else
					print("Modbus invalide coid addr")
					--回复异常消息（非法寄存器数据地址）
					sdata[1] = data[1] --本机地址
					sdata[2] = data[2] + 0x80 --异常的时候功能码加0x80
					sdata[3] = 0x02 --异常码0x02表示设备不支持此数据地址
					local CRC = LIB_CrcCalculate("CRC16_MODBUS", sdata)
					sdata[4] = CRC & 0x00ff --低位在前
					sdata[5] = CRC >> 8 --高位在后
					LIB_Uart1BlockSend(sdata)
				end
			else
				print("Modbus invalide function code")
				--回复异常消息（非法功能码）
				sdata[1] = data[1] --本机地址
				sdata[2] = data[2] + 0x80 --异常的时候功能码加0x80
				sdata[3] = 0x01 --异常码0x01表示设备不支持此功能码
				local CRC = LIB_CrcCalculate("CRC16_MODBUS", sdata)
				sdata[4] = CRC & 0x00ff --低位在前
				sdata[5] = CRC >> 8 --高位在后
				LIB_Uart1BlockSend(sdata)
			end
		end
	end
end

--定义10毫秒定时器的回调函数，函数名字必须是LIB_10msTimerCallback
function LIB_10msTimerCallback()
	LIB_GpioToggle("D11") --喂硬件看门狗
	LoopDelayMsCnt = LoopDelayMsCnt + 10
	SysRunTimeMs = SysRunTimeMs + 10
	--处理通信成功指示灯倒计时
	if CommLedOnTimeMs > 0 then
		CommLedOnTimeMs = CommLedOnTimeMs - 10
		if CommLedOnTimeMs <= 0 then
			--通信成功指示灯熄灭
			LIB_WS2812LedsCtrl("UPDATE_ONE_CACHE", 1, 0, 0, 0)
			LIB_WS2812LedsCtrl("FLUSH")
		end
	end

end

--lua日志关闭
--LIB_LuaTfLogDisable()
--使能USB print输出日志功能
LIB_UsbConfig("CDC")
--设置0.96寸oled模块占用SCL1和SDA1引脚
if OLED_Enable == 1 then
	LIB_0_96_OledConfig("IIC1")
end
--配置D11输出,控制看门狗
LIB_GpioOutputConfig("D11", "STANDARD")
LIB_GpioToggle("D11") --喂硬件看门狗
--使能系统10毫秒定时器开始工作
LIB_10msTimerConfig("ENABLE")
--设置WS2812 Led灯占用D9端口，一共串联1个WS2812灯珠
LIB_WS2812LedsConfig("D9", 1)
--将LED灯按RBG的色值闪烁5次表示上电
for i=1, 5 do
  LIB_WS2812LedsCtrl("UPDATE_ONE_CACHE", 1, RGB[1], RGB[2], RGB[3])
  LIB_WS2812LedsCtrl("FLUSH")
  DelayMS(200)
  LIB_WS2812LedsCtrl("UPDATE_ONE_CACHE", 1, 0, 0, 0)
  LIB_WS2812LedsCtrl("FLUSH")
  DelayMS(200)
end
--配置D0输出,控制继电器Y0
LIB_GpioOutputConfig("D0", "STANDARD")
--配置D0输出,控制继电器Y1
LIB_GpioOutputConfig("D1", "STANDARD")
--配置D0输出,控制继电器Y2
LIB_GpioOutputConfig("D2", "STANDARD")
--Uart1作为485接口初始化
LIB_Uart1Rs485Config(Baudrate, "D8")

--进入大循环
while GC(1) == true do
	--modbus从机通信数据处理
	ModbusSlaveProcess()
	--Oled显示更新
	OledDisplay()
end
